﻿// MyProject.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
//公众号：程序员速成 ，内含一辈子都让你感激自己的优质视频教程，欢迎关注

#include <iostream>
#include <list>
#include <map>

#ifdef _DEBUG   //只在Debug（调试）模式下
#ifndef DEBUG_NEW
#define DEBUG_NEW new(_NORMAL_BLOCK,__FILE__,__LINE__) //重新定义new运算符
#define new DEBUG_NEW
#endif
#endif

//#include <boost/type_index.hpp>
using namespace std;
//#pragma warning(disable : 4996) 

namespace _nmsp1
{
	enum EnumColor //棋子类型
	{
		Black, //黑
		White  //白
	};
	struct Position //棋子位置
	{
		int m_x;
		int m_y;
		Position(int tmpx, int tmpy) :m_x(tmpx), m_y(tmpy) {} //构造函数			
	};
	class Piece //棋子
	{
	public:
		Piece(EnumColor tmpcolor, Position tmppos) :m_color(tmpcolor), m_pos(tmppos) {} //构造函数

		void draw() //棋子的绘制
		{
			if (m_color == Black)
			{
				cout << "在位置：（" << m_pos.m_x << "，" <<  m_pos.m_y << "）处绘制了一个黑色棋子！" << endl;
				//....这里可以是一系列用于具体绘制的代码
			}
			else
			{
				cout << "在位置：（" << m_pos.m_x <<  "，" << m_pos.m_y << "）处绘制了一个白色棋子！" << endl;
				//....这里可以是一系列用于具体绘制的代码
			}
		}
	private:
		EnumColor m_color; //棋子颜色
		Position m_pos; //棋子位置
	};

	
}
namespace _nmsp2
{
	enum EnumColor //棋子类型
	{
		Black, //黑
		White  //白
	};
	struct Position //棋子位置
	{
		int m_x;
		int m_y;
		Position(int tmpx, int tmpy) :m_x(tmpx), m_y(tmpy) {} //构造函数			
	};

	class Piece //棋子抽象类
	{
	public:
		virtual ~Piece() {} //做父类时析构函数应该为虚函数
	public:
		virtual void draw(Position tmppos) = 0;
	};

	class BlackPiece :public Piece //黑色棋子
	{
	public:
		//int ins;
		virtual void draw(Position tmppos)
		{
			cout << "在位置：（" << tmppos.m_x << "，" << tmppos.m_y << "）处绘制了一个黑色棋子！" << endl;
			//....这里可以是一系列用于具体绘制的代码
		}
	};

	class WhitePiece :public Piece //白色棋子
	{
	public:
		virtual void draw(Position tmppos)
		{
			cout << "在位置：（" << tmppos.m_x << "，" << tmppos.m_y << "）处绘制了一个白色棋子！" << endl;
			//....这里可以是一系列用于具体绘制的代码
		}
	};

	class pieceFactory //创建棋子的工厂
	{
	public:
		virtual ~pieceFactory() 
		{
			//释放内存
			for (auto iter = m_FlyWeightMap.begin(); iter != m_FlyWeightMap.end(); ++iter)
			{
				Piece* tmpfw = iter->second;
				delete tmpfw;
			}
			m_FlyWeightMap.clear(); //可有可无
		}

		Piece* getFlyWeight(EnumColor tmpcolor) //获取享元对象，也就是获取被共享的棋子对象
		{
			auto iter = m_FlyWeightMap.find(tmpcolor);
			if (iter == m_FlyWeightMap.end())
			{
				//没有该享元对象，那么就创建出来
				Piece* tmpfw = nullptr;
				if (tmpcolor == Black) //黑子
				{
					tmpfw = new BlackPiece();
				}
				else //白子
				{
					tmpfw = new WhitePiece();
				}
				m_FlyWeightMap.insert(make_pair(tmpcolor, tmpfw)); //以棋子颜色枚举值作为key，增加条目到map中
				return tmpfw;
			}
			else
			{
				return iter->second;
			}
		}

	private:
		std::map<EnumColor, Piece*> m_FlyWeightMap; //享元池，用map容器来保存所有的享元对象，一共就两个享元对象（黑色棋子一个，白色棋子一个）
	};

}

int main()
{
	_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);//程序退出时检测内存泄漏并显示到“输出”窗口
	

	//第13章 享元（Flyweight）模式:蝇量模式，结构型模式，解决的是面向对象程序设计的性能问题
	//享元：被共享的单元或者被共享的对象。Flyweight——轻量级（占用的内存更少）。
	//设计思想：当需要某个对象时，尽量共用已经创建出的同类对象从而避免频繁使用new创建同类或者相似的对象。
	//（1）从一个典型的范例开始
	//享元对象（被共享的对象）：黑棋，白棋
	//（2）引入享元（Flyweight）模式
	//该模式避免了程序中出现大量相同或者相似的对象，通过共享对象的方式实现相似对象的重用。
	//定义：运用共享技术有效地支持大量细粒度的对象（的复用）。

	//内部状态：存储在享元对象内部，一直不会发生改变的状态。这种状态可以被共享。
	//外部状态：随着外部环境和各种动作因素的改变而发生改变的状态，这种状态不可以被共享。
	// 
	//享元模式的目的：减少对象数量，节省内存，提高程序运行效率。

	//三种角色：
	//a)Flyweight(抽象享元类）：Piece类
	//b)ConcreteFlyweight（具体享元类）：BlackPiece、WhitePiece。
	//c)FlyWeightFactory（享元工厂类）：pieceFactory

	//使用情形：
	//a)程序中有大量相同或者相似对象造成内存大量消耗。
	//b)对象的大部分状态都是或者都可以转变成外部状态，通过参数传入到对象中。
	//c)享元池的引入增加了程序实现的复杂性，当然也有一定内存开销，使用享元模式要衡量性价比。

	//不要将享元模式  对象池，连接池，线程池等混为一谈。





	/*
	std::list<_nmsp1::Piece*> piecelist; 

	_nmsp1::Piece* p_piece1 = new _nmsp1::Piece(_nmsp1::Black, _nmsp1::Position(3, 3)); //黑色落子到3,3位置
	p_piece1->draw();

	_nmsp1::Piece* p_piece2 = new _nmsp1::Piece(_nmsp1::White, _nmsp1::Position(5, 5)); //白色落子到5,5位置
	p_piece2->draw();

	_nmsp1::Piece* p_piece3 = new _nmsp1::Piece(_nmsp1::Black, _nmsp1::Position(4, 6)); //黑色落子到4,6位置
	p_piece3->draw();

	_nmsp1::Piece* p_piece4 = new _nmsp1::Piece(_nmsp1::White, _nmsp1::Position(5, 7)); //白色落子到5,7位置
	p_piece4->draw();
	//.....


	piecelist.push_back(p_piece1);
	piecelist.push_back(p_piece2);
	piecelist.push_back(p_piece3);
	piecelist.push_back(p_piece4);


	//释放资源
	delete p_piece1;
	delete p_piece2;
	delete p_piece3;
	delete p_piece4;
	*/

	_nmsp2::pieceFactory* pfactory = new _nmsp2::pieceFactory();

	_nmsp2::Piece* p_piece1 = pfactory->getFlyWeight(_nmsp2::Black);
	p_piece1->draw(_nmsp2::Position(3, 3));  //黑子落子到3,3位置

	_nmsp2::Piece* p_piece2 = pfactory->getFlyWeight(_nmsp2::White);
	p_piece2->draw(_nmsp2::Position(5, 5));  //白子落子到5,5位置

	_nmsp2::Piece* p_piece3 = pfactory->getFlyWeight(_nmsp2::Black);
	p_piece3->draw(_nmsp2::Position(4, 6));  //黑子落子到4,6位置

	_nmsp2::Piece* p_piece4 = pfactory->getFlyWeight(_nmsp2::White);
	p_piece4->draw(_nmsp2::Position(5, 7));  //白子落子到5,7位置


	//释放资源
	delete  pfactory;
	
	return 0;
}

// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
// 调试程序: F5 或调试 >“开始调试”菜单

// 入门使用技巧: 
//   1. 使用解决方案资源管理器窗口添加/管理文件
//   2. 使用团队资源管理器窗口连接到源代码管理
//   3. 使用输出窗口查看生成输出和其他消息
//   4. 使用错误列表窗口查看错误
//   5. 转到“项目”>“添加新项”以创建新的代码文件，或转到“项目”>“添加现有项”以将现有代码文件添加到项目
//   6. 将来，若要再次打开此项目，请转到“文件”>“打开”>“项目”并选择 .sln 文件

